package com.ruoyi.framework.security.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.wechat.WechatConstants;
import com.ruoyi.common.core.domain.entity.wechat.WechatUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.system.service.wechat.IWechatUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 微信小程序token过滤器 验证token有效性
 * 此处的token非JWT的TOKEN，主要考虑到简单易用以及降低token续命的复杂度
 * 因此曹勇了redis来伪实现token
 */
@Component
@Slf4j
public class WechatJwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IWechatUserService wechatUserService;

    /**
     * 排除接口，不需要校验token
     */
    @Value("${wechat.token_filter.exclude_api}")
    private String excludeApiStr;

    private Set<String> excludeApiSet = Collections.EMPTY_SET;

    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    @PostConstruct
    public void init() {
        if (StrUtil.isNotBlank(excludeApiStr)) {
            log.info("exclude api info [{}]", excludeApiStr);
            excludeApiSet = Arrays.stream(excludeApiStr.split(",")).map(String::trim).collect(Collectors.toSet());
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        String requestURI = request.getRequestURI();

        //只要拦截微信业务功能接口就可以
        if (!requestURI.startsWith("/wechat")) {
            chain.doFilter(request, response);
            return;
        }

        if (excludeApiSet.contains(requestURI)) {
            chain.doFilter(request, response);
            return;
        }

        String tokenId = request.getHeader(header);
        if (StrUtil.isBlank(tokenId)) {
            tokenId = request.getParameter(header);
            if (StrUtil.isBlank(tokenId)) {
                errorDispatcher(request, response, "wechat.token.is.empty");
                return;
            }
        }

        Object cacheObject = redisCache.getCacheObject(WechatConstants.WECHAT_TOKEN_KEY_PREFIX + tokenId);
        if (ObjectUtil.isEmpty(cacheObject)) {
            errorDispatcher(request, response, "wechat.token.is.empty");
            return;
        }

        Long wechatUsedrId = Long.parseLong(StrUtil.toString(cacheObject));

        WechatUser wechatUser = wechatUserService.getById(wechatUsedrId);
        if (ObjectUtil.isEmpty(wechatUser)) {
            errorDispatcher(request, response, "wechat.user.not.exist");
            return;
        }

        if (!wechatUser.dateNormal()) {
            errorDispatcher(request, response, "wechat.user.is.delete");
            return;
        }

        request.setAttribute(WechatConstants.WECHAT_TOKEN_USERID, wechatUsedrId);
        request.setAttribute(WechatConstants.WECHAT_TOKEN_USER_ROLEID, wechatUser.getRoleId());
        request.setAttribute(WechatConstants.WECHAT_TOKEN_USER_DEPTID, wechatUser.getDeptId());
        chain.doFilter(request, response);
    }

    private void errorDispatcher(HttpServletRequest request, HttpServletResponse response, String errorCode) throws ServletException, IOException {
        // 异常捕获，发送到error controller
        request.setAttribute("filter.error", errorCode);
        //将异常分发到/error/exthrow控制器
        request.getRequestDispatcher("/wechat/error/filter/exthrow").forward(request, response);
    }
}
